package com.caishi.lkx.user.login;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwk.InvalidPublicKeyException;
import com.auth0.jwk.Jwk;
import com.caishi.lkx.user.dto.LoginDto;
import com.caishi.lkx.user.ienum.type.UserAccountType;
import com.caishi.lkx.user.ienum.type.UserType;
import com.caishi.lkx.user.model.IUserModel;
import com.caishi.lkx.user.model.LoginInfoModel;
import com.caishi.lkx.user.model.MemberModel;
import com.caishi.lkx.user.service.ILoginInfoService;
import com.zzw.common.exception.BizException;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.utils.MD5Util;
import com.caishi.lkx.user.UserResultCode;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.DefaultClaims;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.security.PublicKey;

@Slf4j
@Service(value = "iphoneLoginService")
public class IphoneLogin implements LoginService {

    @Resource
    private ILoginInfoService loginInfoService;

    @Resource
    private LoginServiceComposite loginServiceComposite;

    @Override
    public LoginInfoModel loginBase(LoginDto loginDto, HttpServletRequest request, HttpServletResponse response) throws BizException {
        IOSUserInfo userInfo = appleLogin(loginDto.getCredential());
        String appleId = userInfo.getSub();
        String userAccount = MD5Util.MD5Encode(appleId);
        LoginInfoModel infoModel = loginInfoService.selectLoginInfoByCredentialsAndAccount(userInfo.getSub(), userAccount, UserAccountType.iphone);
        if (infoModel == null) {
            // 苹果登录账号不存在时自动创建账号
            infoModel = LoginInfoModel.builder()
                    .account(userAccount)
                    .certificate(userInfo.getSub())
                    .loginType(UserAccountType.iphone)
                    .lock(false)
                    .enable(true)
                    .build();
            IUserModel<?> model = MemberModel.builder()
                    .email(userInfo.getEmail())
                    .build();
            model.addUserType(UserType.member);
            model = loginServiceComposite.register(infoModel, model);
            infoModel.setUserId(model.getId());
        }
        return checkLoginInfo(infoModel, loginDto);
    }

    public IOSUserInfo appleLogin(String identityToken) {
        try {
            //验证identityToken
            JSONObject key = verifyAndGetKey(identityToken);
            if (key == null) {
                throw new BizRuntimeException(UserResultCode.loginFail);
            }
            Object objectResult = IOSInfo(identityToken, key);
            IOSUserInfo iosUserInfo = new IOSUserInfo();
            if (objectResult instanceof DefaultClaims) {
                DefaultClaims claims = (DefaultClaims) objectResult;
                iosUserInfo.setSub((String) claims.get("sub"));
                iosUserInfo.setEmail((String) claims.get("email"));
                iosUserInfo.setCHash((String) claims.get("c_hash"));
            } else {
                throw new BizRuntimeException(UserResultCode.loginFail);
            }
            return iosUserInfo;
        } catch (Exception e) {
            log.error("app wxLogin error:" + e.getMessage(), e);
            throw new BizRuntimeException(UserResultCode.loginFail);
        }

    }

    public static JSONObject verifyAndGetKey(String jwt) throws Exception {
        String url = "https://appleid.apple.com/auth/keys";
        RestTemplate restTemplate = new RestTemplate();
        JSONObject json = restTemplate.getForObject(url, JSONObject.class);
        if (json == null) {
            return null;
        }
        JSONArray arr = json.getJSONArray("keys");
        if (arr == null) {
            return null;
        }
        for (int i = 0; i < arr.size(); i++) {
            if (verifyExc(jwt, arr.getJSONObject(i))) {
                return arr.getJSONObject(i);
            }
        }
        return null;

    }

    public static Object IOSInfo(String jwt, JSONObject key) throws Exception {
        JwtParser jwtParser = parser(jwt, key);
        Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
        if (claim != null && claim.getBody().containsKey("auth_time")) {
            return claim.getBody();
        }
        throw new BizRuntimeException(UserResultCode.loginFail);
    }

    /**
     * 对前端传来的identityToken进行验证
     *
     * @param jwt     对应前端传来的 identityToken
     * @param authKey 苹果的公钥 authKey
     * @return
     * @throws Exception
     */
    public static Boolean verifyExc(String jwt, JSONObject authKey) throws Exception {
        JwtParser jwtParser = parser(jwt, authKey);
        try {
            Jws<Claims> claim = jwtParser.parseClaimsJws(jwt);
            return claim != null && claim.getBody().containsKey("auth_time");
        } catch (Exception e) {
            return false;
        }
    }

    private static JwtParser parser(String jwt, JSONObject authKey) throws InvalidPublicKeyException {
        Jwk jwa = Jwk.fromValues(authKey);
        PublicKey publicKey = jwa.getPublicKey();

        String aud = "";
        String sub = "";
        if (jwt.split("\\.").length > 1) {
            String claim = new String(Base64.decodeBase64(jwt.split("\\.")[1]));
            aud = JSONObject.parseObject(claim).get("aud").toString();
            sub = JSONObject.parseObject(claim).get("sub").toString();
        }
        JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
        jwtParser.requireIssuer("https://appleid.apple.com");
        jwtParser.requireAudience(aud);
        jwtParser.requireSubject(sub);
        return jwtParser;
    }

}

@Getter
@Setter
class IOSUserInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 苹果iss(签发机构网址)
     */
    private String iss;

    /**
     * bundle id
     */
    private String aud;

    /**
     * int 过期时间戳
     */
    private String exp;

    /**
     * 签发时间
     */
    private String iat;

    /**
     * user id
     */
    private String sub;

    /**
     * email
     */
    private String email;

    /**
     * email 是否确认了
     */
    private boolean emailVerified;

    /**
     * emails授权时间
     */
    private String authTime;

    /**
     * 一段哈希
     */
    private String cHash;
}
